//
// property.cs: Property based handlers
//
// Authors: Miguel de Icaza (miguel@gnu.org)
//          Martin Baulig (martin@ximian.com)
//          Marek Safar (marek.safar@seznam.cz)
//
// Dual licensed under the terms of the MIT X11 or GNU GPL
//
// Copyright 2001, 2002, 2003 Ximian, Inc (http://www.ximian.com)
// Copyright 2004-2008 Novell, Inc
// Copyright 2011 Xamarin Inc
//

using System;
using System.Collections.Generic;
using System.Text;
using Mono.CompilerServices.SymbolWriter;

using System.Reflection;
using System.Reflection.Emit;

namespace Teal.Compiler {
    
    public partial class PropertyReference :IInterfaceMemberSpec {

        MethodReference set, get;

        public MethodReference Get {
            get {
                return get;
            }
            set {
                get = value;
                get.IsAccessor = true;
            }
        }

        public MethodReference Set {
            get {
                return set;
            }
            set {
                set = value;
                set.IsAccessor = true;
            }
        }

        public bool HasDifferentAccessibility {
            get {
                return getAccessor != null && setAccessor != null &&
                    (Get.modifiers & Modifiers.ACCESSIBILITY) != (Set.modifiers & Modifiers.ACCESSIBILITY);
            }
        }

        public TypeReference MemberType2 {
            get {
                return returnType;
            }
        }

        public override List<MissingTypeSpecReference> ResolveMissingDependencies(MemberReference caller) {
            return returnType.ResolveMissingDependencies(this);
        }
    }

    //
    // Properties and Indexers both generate PropertyBuilders, we use this to share 
    // their common bits.
    //
    abstract public partial class PropertyOrIndexerDefinition : MethodOrPropertyDefinition {

        public PropertyOrIndexerDefinition() { }

        public class GetMethod : PropertyMethod {
            internal const string Prefix = "get_";

            public GetMethod(PropertyOrIndexerDefinition method, Modifiers modifiers, MemberAnnotationList attrs, Location2 loc)
                : base(method, Prefix, modifiers, attrs, loc) {
            }

            public override void Define(MemberContainerDefinition parent) {
                base.Define(parent);

                Spec = new MethodReference();

                method_data = new MethodData();

                method_data.Define(parent.PartialContainer, method.GetFullName(MemberName));
            }

            public override TypeReference ReturnType {
                get {
                    return method.MemberType2;
                }
            }

            public override ParametersCompiled ParameterInfo {
                get {
                    return ParametersCompiled.EmptyReadOnlyParameters;
                }
            }

            public override void defineMember() {
                throw new NotImplementedException();
            }

            public override void createTypeBuilder() {
                throw new NotImplementedException();
            }

            public override void emitBody() {
                throw new NotImplementedException();
            }

            public override void closeTypeBuilder() {
                throw new NotImplementedException();
            }
        }

        public class SetMethod : PropertyMethod {
            internal const string Prefix = "set_";

            protected ParametersCompiled parameters;

            public SetMethod(PropertyOrIndexerDefinition method, Modifiers modifiers, ParametersCompiled parameters, MemberAnnotationList attrs, Location2 loc)
                : base(method, Prefix, modifiers, attrs, loc) {
                this.parameters = parameters;
            }

            protected override void ApplyToExtraTarget(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
                if (a.Target == AttributeTargets.Parameter) {
                    parameters[0].ApplyAttributeBuilder(a, ctor, cdata, pa);
                    return;
                }

                base.ApplyToExtraTarget(a, ctor, cdata, pa);
            }

            public override ParametersCompiled ParameterInfo {
                get {
                    return parameters;
                }
            }

            public override void Define(MemberContainerDefinition parent) {
                parameters.Resolve(this);

                base.Define(parent);

                Spec = new MethodReference();

                method_data = new MethodData();

                method_data.Define(parent.PartialContainer, method.GetFullName(MemberName));
            }

            public override TypeReference ReturnType {
                get {
                    return Compiler.BuiltinTypes.Void;
                }
            }

            public override void defineMember() {
                throw new NotImplementedException();
            }

            public override void createTypeBuilder() {
                throw new NotImplementedException();
            }

            public override void emitBody() {
                throw new NotImplementedException();
            }

            public override void closeTypeBuilder() {
                throw new NotImplementedException();
            }
        }

        public abstract class PropertyMethod : AbstractPropertyEventMethod {
            const Modifiers AllowedModifiers =
                Modifiers.@public |
                Modifiers.@protected |
                Modifiers.@private;

            protected readonly PropertyOrIndexerDefinition method;
            protected MethodAttributes flags;

            public PropertyMethod(PropertyOrIndexerDefinition method, string prefix, Modifiers modifiers, MemberAnnotationList attrs, Location2 loc)
                : base(method, prefix, attrs, loc) {
                this.method = method;
                this.ModFlags = ModifiersExtensions.Check(AllowedModifiers, modifiers, 0, loc, Compiler.Report);
                this.ModFlags |= (method.ModFlags & (Modifiers.@static));
            }

            public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
                if (a.Type == pa.MethodImpl) {
                    method.is_external_implementation = a.IsInternalCall();
                }

                base.ApplyAttributeBuilder(a, ctor, cdata, pa);
            }

            public virtual void Define(MemberContainerDefinition parent) {
                var container = parent.PartialContainer;

                //
                // Check for custom access modifier
                //
                if ((ModFlags & Modifiers.ACCESSIBILITY) == 0) {
                    ModFlags |= method.ModFlags;
                    flags = method.flags;
                } else {
                    if (container.Kind == MemberKind.Interface)
                        Compiler.Report.Error(275, startLocation, "`{0}': accessibility modifiers may not be used on accessors in an interface",
                            GetSignatureForError());
                    else if ((method.ModFlags & Modifiers.@abstract) != 0 && (ModFlags & Modifiers.@private) != 0) {
                        Compiler.Report.Error(442, startLocation, "`{0}': abstract properties cannot have private accessors", GetSignatureForError());
                    }

                    CheckModifiers(ModFlags);
                    ModFlags |= (method.ModFlags & (~Modifiers.ACCESSIBILITY));
                    ModFlags |= Modifiers.PROPERTY_CUSTOM;
                    flags = ModifiersExtensions.MethodAttr(ModFlags);
                    flags |= (method.flags & (~MethodAttributes.MemberAccessMask));
                }

              //  base.checkAbstractAndExtern(block != null);
                //CheckProtectedModifier ();

                if (block != null) {
                    if (block.IsIterator)
                        Iterator.CreateIterator(this, container.PartialContainer, ModFlags);
                }
            }

            public bool HasCustomAccessModifier {
                get {
                    return (ModFlags & Modifiers.PROPERTY_CUSTOM) != 0;
                }
            }

            public PropertyOrIndexerDefinition Property {
                get {
                    return method;
                }
            }

            public override ObsoleteAttribute GetAttributeObsolete() {
                return method.GetAttributeObsolete();
            }

            public override string GetSignatureForError() {
                return method.GetSignatureForError() + "." + prefix.Substring(0, 3);
            }

            void CheckModifiers(Modifiers modflags) {
                if (!ModifiersExtensions.IsRestrictedModifier(modflags & Modifiers.ACCESSIBILITY, method.ModFlags & Modifiers.ACCESSIBILITY)) {
                    Compiler.Report.Error(273, startLocation,
                        "The accessibility modifier of the `{0}' accessor must be more restrictive than the modifier of the property or indexer `{1}'",
                        GetSignatureForError(), method.GetSignatureForError());
                }
            }
        }

        PropertyMethod get, set, first;

        #region Properties

        public PropertyMethod AccessorFirst {
            get {
                return first;
            }
        }

        public PropertyMethod AccessorSecond {
            get {
                return first == get ? set : get;
            }
        }

        //public override Variance ExpectedMemberTypeVariance {
        //    get {
        //        return (get != null && set != null) ?
        //            Variance.None : set == null ?
        //            Variance.Covariant :
        //            Variance.Contravariant;
        //    }
        //}

        public PropertyMethod Get {
            get {
                return get;
            }
            set {
                get = value;
                if (first == null)
                    first = value;

                //Parent.AddNameToContainer (get, get.MemberName.Basename);
            }
        }

        public PropertyMethod Set {
            get {
                return set;
            }
            set {
                set = value;
                if (first == null)
                    first = value;

                //Parent.AddNameToContainer (set, set.MemberName.Basename);
            }
        }

        #endregion

        public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
            if (a.HasSecurityAttribute) {
                a.Error_InvalidSecurityParent();
                return;
            }

            builder.SetCustomAttribute((ConstructorInfo)ctor.GetMetaInfo(), cdata);
        }

        protected bool DefineAccessors() {
            first.Define(containerType);
            if (AccessorSecond != null)
                AccessorSecond.Define(containerType);

            return true;
        }

        public override bool IsUsed {
            get {
                return true;
            }
        }

        //
        //   Represents header string for documentation comment.
        //
    }

    public partial class PropertyDefinition : PropertyOrIndexerDefinition {
     
        public PropertyDefinition()  {

        }

        public Expression Initializer { get; set; }

        public override bool Define() {
            if (!base.Define())
                return false;

            if (!DefineAccessors())
                return false;

            if (AccessorSecond == null) {
                PropertyMethod pm;
                if (AccessorFirst is GetMethod)
                    pm = new SetMethod(this, 0, ParametersCompiled.EmptyReadOnlyParameters, null, startLocation2);
                else
                    pm = new GetMethod(this, 0, null, startLocation2);

                //Parent.AddNameToContainer (pm, pm.MemberName.Basename);
            }

            if (!CheckBase())
                return false;

            return true;
        }

    }

    /// <summary>
    /// For case when event is declared like property (with add and remove accessors).
    /// </summary>
    public class EventProperty : EventDefinition {
        public abstract class AEventPropertyAccessor : AEventAccessor {
            protected AEventPropertyAccessor(EventProperty method, string prefix, MemberAnnotationList attrs, Location2 loc)
                : base(method, prefix, attrs, loc) {
            }

            public override void Define(MemberContainerDefinition ds) {
               // base.checkAbstractAndExtern(block != null);
                base.Define(ds);
            }

            public override string GetSignatureForError() {
                return method.GetSignatureForError() + "." + prefix.Substring(0, prefix.Length - 1);
            }
        }

        public sealed class AddDelegateMethod : AEventPropertyAccessor {
            public AddDelegateMethod(EventProperty method, MemberAnnotationList attrs, Location2 loc)
                : base(method, AddPrefix, attrs, loc) {
            }

            public override void defineMember() {
                throw new NotImplementedException();
            }

            public override void createTypeBuilder() {
                throw new NotImplementedException();
            }

            public override void emitBody() {
                throw new NotImplementedException();
            }

            public override void closeTypeBuilder() {
                throw new NotImplementedException();
            }
        }

        public sealed class RemoveDelegateMethod : AEventPropertyAccessor {
            public RemoveDelegateMethod(EventProperty method, MemberAnnotationList attrs, Location2 loc)
                : base(method, RemovePrefix, attrs, loc) {
            }

            public override void defineMember() {
                throw new NotImplementedException();
            }

            public override void createTypeBuilder() {
                throw new NotImplementedException();
            }

            public override void emitBody() {
                throw new NotImplementedException();
            }

            public override void closeTypeBuilder() {
                throw new NotImplementedException();
            }
        }

        public EventProperty(TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, MemberAnnotationList attrs)
            : base(parent, type, mod_flags, name, attrs) {
        }


        public override void emitBody() {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// Event is declared like field.
    /// </summary>
    public class EventFieldDefinition : EventDefinition {
        abstract class EventFieldAccessor : AEventAccessor {
            protected EventFieldAccessor(EventFieldDefinition method, string prefix)
                : base(method, prefix, null, method.startLocation2) {
            }

            protected abstract MethodReference GetOperation(Location2 loc);

            public override void Emit(TypeDefinition parent) {
                if ((method.ModFlags & (Modifiers.@abstract | Modifiers.@extern)) == 0) {
                    block = new ToplevelBlock(ParameterInfo, startLocation) {
                        IsCompilerGenerated = true
                    };
                    FabricateBodyStatement();
                }

                base.Emit(parent);
            }

            void FabricateBodyStatement() {
                //
                // Delegate obj1 = backing_field
                // do {
                //   Delegate obj2 = obj1;
                //   obj1 = Interlocked.CompareExchange (ref backing_field, Delegate.Combine|Remove(obj2, value), obj1);
                // } while ((object)obj1 != (object)obj2)
                //

                var field_info = ((EventFieldDefinition)method).backing_field;
                FieldExpr f_expr = new FieldExpr(field_info, startLocation);
                if (!IsStatic)
                    f_expr.InstanceExpression = new CompilerGeneratedThis(container.CurrentType, startLocation);

                var obj1 = LocalVariable.CreateCompilerGenerated(field_info.MemberType2, block, startLocation);
                var obj2 = LocalVariable.CreateCompilerGenerated(field_info.MemberType2, block, startLocation);

                block.AddStatement(new StatementExpression(new SimpleAssign(new LocalVariableReference(obj1, startLocation), f_expr)));

                var cond = new BooleanExpression(new BinaryExpression(BinaryExpression.Operator.Inequality,
                    new Cast(new TypeExpression(Compiler.BuiltinTypes.Object, startLocation), new LocalVariableReference(obj1, startLocation), startLocation),
                    new Cast(new TypeExpression(Compiler.BuiltinTypes.Object, startLocation), new LocalVariableReference(obj2, startLocation), startLocation)));

                var body = new ExplicitBlock(block, startLocation, startLocation);
                block.AddStatement(new DoWhileStatement(body, cond, startLocation, startLocation));

                body.AddStatement(new StatementExpression(
                    new SimpleAssign(new LocalVariableReference(obj2, startLocation), new LocalVariableReference(obj1, startLocation))));

                var args_oper = new Arguments(2);
                args_oper.Add(new Argument(new LocalVariableReference(obj2, startLocation)));
                args_oper.Add(new Argument(block.GetParameterReference(0, startLocation)));

                var op_method = GetOperation(startLocation);

                var args = new Arguments(3);
                args.Add(new Argument(f_expr, Argument.AType.Ref));
                args.Add(new Argument(new Cast(
                    new TypeExpression(field_info.MemberType2, startLocation),
                    new Invocation(MethodGroupExpr.CreatePredefined(op_method, op_method.containerType, startLocation), args_oper),
                    startLocation)));
                args.Add(new Argument(new LocalVariableReference(obj1, startLocation)));

                var cas = containerModule.PredefinedMembers.InterlockedCompareExchange_T.Resolve(startLocation);
                if (cas == null)
                    return;

                body.AddStatement(new StatementExpression(new SimpleAssign(
                    new LocalVariableReference(obj1, startLocation),
                    new Invocation(MethodGroupExpr.CreatePredefined(cas, cas.containerType, startLocation), args))));
            }
        }

        sealed class AddDelegateMethod : EventFieldAccessor {
            public AddDelegateMethod(EventFieldDefinition method) :
                base(method, AddPrefix) {
            }

            protected override MethodReference GetOperation(Location2 loc) {
                return containerModule.PredefinedMembers.DelegateCombine.Resolve(loc);
            }

            public override void defineMember() {
                throw new NotImplementedException();
            }

            public override void createTypeBuilder() {
                throw new NotImplementedException();
            }

            public override void emitBody() {
                throw new NotImplementedException();
            }

            public override void closeTypeBuilder() {
                throw new NotImplementedException();
            }
        }

        sealed class RemoveDelegateMethod : EventFieldAccessor {
            public RemoveDelegateMethod(EventFieldDefinition method) :
                base(method, RemovePrefix) {
            }

            protected override MethodReference GetOperation(Location2 loc) {
                return containerModule.PredefinedMembers.DelegateRemove.Resolve(loc);
            }

            public override void defineMember() {
                throw new NotImplementedException();
            }

            public override void createTypeBuilder() {
                throw new NotImplementedException();
            }

            public override void emitBody() {
                throw new NotImplementedException();
            }

            public override void closeTypeBuilder() {
                throw new NotImplementedException();
            }
        }


        static readonly string[] attribute_targets = new string[] { "event", "field", "method" };
        static readonly string[] attribute_targets_interface = new string[] { "event", "method" };

        Expression initializer;
        FieldDefinition backing_field;
        List<Variable> declarators;

        public EventFieldDefinition(TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, MemberAnnotationList attrs)
            : base(parent, type, mod_flags, name, attrs) {
            Add = new AddDelegateMethod(this);
            Remove = new RemoveDelegateMethod(this);
        }

        #region Properties

        bool HasBackingField {
            get {
                return !isInterfaceMember && (ModFlags & Modifiers.@abstract) == 0;
            }
        }

        public Expression Initializer {
            get {
                return initializer;
            }
            set {
                initializer = value;
            }
        }

        #endregion

        //public void AddDeclarator (FieldDeclarator declarator)
        //{
        //    if (declarators == null)
        //        declarators = new List<FieldDeclarator> (2);

        //    declarators.Add (declarator);

        //    Parent.AddNameToContainer (this, declarator.Name.Value);
        //}

        public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
            if (a.Target == AttributeTargets.Field) {
                backing_field.ApplyAttributeBuilder(a, ctor, cdata, pa);
                return;
            }

            if (a.Target == AttributeTargets.Method) {
                int errors = Teal.Compiler.Compiler.errorCount;
                Add.ApplyAttributeBuilder(a, ctor, cdata, pa);
                if (errors == Teal.Compiler.Compiler.errorCount)
                    Remove.ApplyAttributeBuilder(a, ctor, cdata, pa);
                return;
            }

            base.ApplyAttributeBuilder(a, ctor, cdata, pa);
        }

        public override bool Define() {
            var mod_flags_src = ModFlags;

            if (!base.Define())
                return false;

            if (declarators != null) {
                if ((mod_flags_src & Modifiers.DEFAULT_ACCESS_MODIFIER) != 0)
                    mod_flags_src &= ~(Modifiers.ACCESSIBILITY | Modifiers.DEFAULT_ACCESS_MODIFIER);

                var t = new TypeExpression(MemberType2, TypeExpression.Location);
                foreach (var d in declarators) {
                    var ef = new EventFieldDefinition(containerType, t, mod_flags_src, new MemberName(d.name.value, Location2.Null), attributes);

                    if (d.initialiser != null)
                        ef.initializer = d.initialiser;

                    ef.Define();
                    containerType.PartialContainer.Members.Add(ef);
                }
            }

            if (!HasBackingField) {
                SetIsUsed();
                return true;
            }

            backing_field = new FieldDefinition();

            containerType.PartialContainer.Members.Add(backing_field);
            backing_field.Initializer = Initializer;
            backing_field.ModFlags &= ~Modifiers.COMPILER_GENERATED;

            // Call define because we passed fields definition
            backing_field.Define();

            // Set backing field for event fields
            spec.BackingField = backing_field.Spec;

            return true;
        }

        public override void emitBody() {
            throw new NotImplementedException();
        }
    }

    public abstract class EventDefinition : PropertyOrIndexerDefinition {
        public abstract class AEventAccessor : AbstractPropertyEventMethod {
            protected readonly EventDefinition method;
            readonly ParametersCompiled parameters;

            static readonly string[] attribute_targets = new string[] { "method", "param", "return" };

            public const string AddPrefix = "add_";
            public const string RemovePrefix = "remove_";

            protected AEventAccessor(EventDefinition method, string prefix, MemberAnnotationList attrs, Location2 loc)
                : base(method, prefix, attrs, loc) {
                this.method = method;
                this.ModFlags = method.ModFlags;
                this.parameters = ParametersCompiled.CreateImplicitParameter(method.TypeExpression, loc);
            }

            public bool IsInterfaceImplementation {
                get { return method_data.implementing != null; }
            }

            public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
                if (a.Type == pa.MethodImpl) {
                    method.is_external_implementation = a.IsInternalCall();
                }

                base.ApplyAttributeBuilder(a, ctor, cdata, pa);
            }

            protected override void ApplyToExtraTarget(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
                if (a.Target == AttributeTargets.Parameter) {
                    parameters[0].ApplyAttributeBuilder(a, ctor, cdata, pa);
                    return;
                }

                base.ApplyToExtraTarget(a, ctor, cdata, pa);
            }

            public virtual void Define(MemberContainerDefinition parent) {
                // Fill in already resolved event type to speed things up and
                // avoid confusing duplicate errors
                ((Parameter2)parameters.FixedParameters[0]).Type = method.member_type;
                parameters.Types = new TypeReference[] { method.member_type };

                method_data = new MethodData();

                if (!method_data.Define(parent.PartialContainer, method.GetFullName(MemberName)))
                    return;

                Spec = new MethodReference();
                Spec.IsAccessor = true;
            }

            public override TypeReference ReturnType {
                get {
                    return Compiler.BuiltinTypes.Void;
                }
            }

            public override ObsoleteAttribute GetAttributeObsolete() {
                return method.GetAttributeObsolete();
            }

            public MethodData MethodData {
                get {
                    return method_data;
                }
            }

            public override ParametersCompiled ParameterInfo {
                get {
                    return parameters;
                }
            }
        }

        AEventAccessor add, remove;
        EventBuilder EventBuilder;
        protected EventSpec spec;

        protected EventDefinition(TypeDefinition parent, FullNamedExpression type, Modifiers mod_flags, MemberName name, MemberAnnotationList attrs)
            : base() {
        }

        #region Properties

        public AEventAccessor Add {
            get {
                return this.add;
            }
            set {
                add = value;
                //Parent.AddNameToContainer (value, value.MemberName.Basename);
            }
        }

        //public override Variance ExpectedMemberTypeVariance {
        //    get {
        //        return Variance.Contravariant;
        //    }
        //}

        public AEventAccessor Remove {
            get {
                return this.remove;
            }
            set {
                remove = value;
                //Parent.AddNameToContainer (value, value.MemberName.Basename);
            }
        }
        #endregion

        public override bool Define() {
            if (!base.Define())
                return false;

            if (!MemberType2.IsDelegate) {
                Compiler.Report.Error(66, startLocation2, "`{0}': event must be of a delegate type", GetSignatureForError());
            }

            if (!CheckBase())
                return false;

            //
            // Now define the accessors
            //
            add.Define(containerType);
            remove.Define(containerType);

            EventBuilder = containerType.builder.DefineEvent(GetFullName(MemberName), EventAttributes.None, MemberType2.GetMetaInfo());

            spec = new EventSpec(containerType.reference, this, MemberType2, ModFlags, Add.Spec, remove.Spec);

            containerType.MemberCache.AddMember(this, GetFullName(MemberName), spec);
            containerType.MemberCache.AddMember(this, Add.Spec.name, Add.Spec);
            containerType.MemberCache.AddMember(this, Remove.Spec.name, remove.Spec);

            return true;
        }

        public override void Emit() {
            if (attributes != null) {
                attributes.Emit();
            }

            ConstraintChecker.Check(this, member_type, type_expr.Location);

            Add.Emit(containerType);
            Remove.Emit(containerType);

            base.Emit();
        }

        public override void writeDebugSymbol(MonoSymbolFile file) {
            add.writeDebugSymbol(file);
            remove.writeDebugSymbol(file);
        }

        //
        //   Represents header string for documentation comment.
        //

        public override void resolveBody() {
            throw new NotImplementedException();
        }
    }

    public class EventSpec : MemberReference, IInterfaceMemberSpec {
        MethodReference add, remove;
        FieldReference backing_field;

        public EventSpec(TypeReference declaringType, IMemberDefinition definition, TypeReference eventType, Modifiers modifiers, MethodReference add, MethodReference remove)
            : base(MemberKind.Event, declaringType, definition, modifiers) {
            this.AccessorAdd = add;
            this.AccessorRemove = remove;
            this.MemberType2 = eventType;
        }

        #region Properties

        public MethodReference AccessorAdd {
            get {
                return add;
            }
            set {
                add = value;
            }
        }

        public MethodReference AccessorRemove {
            get {
                return remove;
            }
            set {
                remove = value;
            }
        }

        public FieldReference BackingField {
            get {
                return backing_field;
            }
            set {
                backing_field = value;
            }
        }

        public TypeReference MemberType2 { get; private set; }

        #endregion

        //public override MemberReference InflateMember (TypeParameterInflator inflator)
        //{
        //    var es = (EventSpec) base.InflateMember (inflator);
        //    es.MemberType2 = inflator.Inflate (MemberType2);

        //    if (backing_field != null)
        //        es.backing_field = (FieldReference) backing_field.InflateMember (inflator);

        //    return es;
        //}

        public override List<MissingTypeSpecReference> ResolveMissingDependencies(MemberReference caller) {
            return MemberType2.ResolveMissingDependencies(this);
        }

        public override MemberReference inflateMember(MemberReference genericMember) {
            throw new NotImplementedException();
        }

        public override MemberType memberType {
            get { throw new NotImplementedException(); }
        }
    }

    public partial class IndexerDefinition : PropertyOrIndexerDefinition, IParametersMember {
        public class GetIndexerMethod : GetMethod, IParametersMember {
            ParametersCompiled parameters;

            public GetIndexerMethod(PropertyOrIndexerDefinition property, Modifiers modifiers, ParametersCompiled parameters, MemberAnnotationList attrs, Location2 loc)
                : base(property, modifiers, attrs, loc) {
                this.parameters = parameters;
            }

            public override void Define(MemberContainerDefinition parent) {
                // Disable reporting, parameters are resolved twice
                Compiler.Report.DisableReporting();
                try {
                    parameters.Resolve(this);
                } finally {
                    Compiler.Report.EnableReporting();
                }

                base.Define(parent);
            }

            public override ParametersCompiled ParameterInfo {
                get {
                    return parameters;
                }
            }

            #region IParametersMember Members

            AParametersCollection IParametersMember.Parameters {
                get {
                    return parameters;
                }
            }

            TypeReference IInterfaceMemberSpec.MemberType2 {
                get {
                    return ReturnType;
                }
            }

            #endregion
        }

        public class SetIndexerMethod : SetMethod, IParametersMember {
            public SetIndexerMethod(PropertyOrIndexerDefinition property, Modifiers modifiers, ParametersCompiled parameters, MemberAnnotationList attrs, Location2 loc)
                : base(property, modifiers, parameters, attrs, loc) {
            }

            #region IParametersMember Members

            AParametersCollection IParametersMember.Parameters {
                get {
                    return parameters;
                }
            }

            TypeReference IInterfaceMemberSpec.MemberType2 {
                get {
                    return ReturnType;
                }
            }

            #endregion
        }

        readonly ParametersCompiled parameters2;

        #region Properties

        AParametersCollection IParametersMember.Parameters {
            get {
                return parameters2;
            }
        }

        public ParametersCompiled ParameterInfo {
            get {
                return parameters2;
            }
        }

        #endregion

        public override void ApplyAttributeBuilder(MemberAnnotation a, MethodReference ctor, byte[] cdata, PredefinedAttributes pa) {
            if (a.Type == pa.IndexerName) {
                // Attribute was copied to container
                return;
            }

            base.ApplyAttributeBuilder(a, ctor, cdata, pa);
        }

        public override bool Define() {
            if (!base.Define())
                return false;

            if (attributes != null) {
                MemberAnnotation indexer_attr = attributes.Search(containerModule.PredefinedAttributes.IndexerName);
                if (indexer_attr != null) {
                    var compiling = indexer_attr.Type.MemberDefinition2 as MemberContainerDefinition;
                    if (compiling != null)
                        compiling.Define();

                    if (explicitType != null) {
                        Compiler.Report.Error(415, indexer_attr.Location,
                            "The `{0}' attribute is valid only on an indexer that is not an explicit interface member declaration",
                            indexer_attr.Type.GetSignatureForError());
                    } else if ((ModFlags & Modifiers.@override) != 0) {
                        Compiler.Report.Error(609, indexer_attr.Location,
                            "Cannot set the `IndexerName' attribute on an indexer marked override");
                    } else {
                        string name = indexer_attr.GetIndexerAttributeValue();

                    }
                }
            }

            var ii = InterfaceType;
            if (ii != null) {
                string base_IndexerName = ii.MemberDefinition2.GetAttributeDefaultMember();
              
            }

            //Parent.AddNameToContainer (this, MemberName.Basename);

            flags |= MethodAttributes.HideBySig | MethodAttributes.SpecialName;

            if (!DefineAccessors())
                return false;

            if (!CheckBase())
                return false;

            return true;
        }

        public override bool EnableOverloadChecks(MemberDefinition overload) {
            if (overload is IndexerDefinition) {
                caching_flags |= Flags2.MethodOverloadsExist;
                return true;
            }

            return base.EnableOverloadChecks(overload);
        }

        public override void Emit() {
            parameters2.CheckConstraints(this);

            base.Emit();
        }

        public override string GetSignatureForError() {
            StringBuilder sb = new StringBuilder(containerType.GetSignatureForError());
            if (MemberName.ExplicitInterface != null) {
                sb.Append(".");
                sb.Append(MemberName.ExplicitInterface.GetSignatureForError());
            }

            sb.Append(".this");
            sb.Append(parameters2.GetSignatureForError("[", "]", parameters2.Count));
            return sb.ToString();
        }
    }

    public partial class IndexerReference : PropertyReference, IParametersMember {

        public IndexerReference()
            : base() {

        }
        AParametersCollection parameters2;

        public IndexerReference(TypeReference declaringType, IMemberDefinition definition, TypeReference memberType, AParametersCollection parameters, PropertyInfo info, Modifiers modifiers)
            : base() {
            this.parameters2 = parameters;
        }

        #region Properties
        public AParametersCollection Parameters {
            get {
                return parameters2;
            }
        }
        #endregion

        public override string GetSignatureForError() {
            return containerType.GetSignatureForError() + ".this" + parameters2.GetSignatureForError("[", "]", parameters2.Count);
        }

        public override List<MissingTypeSpecReference> ResolveMissingDependencies(MemberReference caller) {
            var missing = base.ResolveMissingDependencies(caller);

            foreach (var pt in parameters2.Types) {
                var m = pt.GetMissingDependencies(caller);
                if (m == null)
                    continue;

                if (missing == null)
                    missing = new List<MissingTypeSpecReference>();

                missing.AddRange(m);
            }

            return missing;
        }
    }
}
